package net.realsee.gcl.service.impl;

import cn.hutool.core.date.DateUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.http.HttpResponse;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.realsee.common.constant.GclMesConstants;
import net.realsee.common.constant.HttpStatus;
import net.realsee.common.enums.wcs.intf.InterfaceCodeEnum;
import net.realsee.common.enums.wcs.intf.InterfaceResultCodeEnum;
import net.realsee.common.enums.wcs.mes.GclMesRunFlagEnum;
import net.realsee.common.enums.wcs.order.*;
import net.realsee.common.exception.mes.GclMesTaskException;
import net.realsee.common.utils.InterfaceUtils;
import net.realsee.domain.wcs.data.WcsZoneTaskTemp;
import net.realsee.domain.wcs.intf.bo.InterfaceRequest;
import net.realsee.domain.wcs.intf.bo.InterfaceResponse;
import net.realsee.domain.wcs.rcs.hik.HikRcsOperateResult;
import net.realsee.domain.wcs.warehouse.bo.LocationFullInfoBo;
import net.realsee.domain.wcs.work.WcsOrderDetail;
import net.realsee.domain.wcs.work.WcsOrderHeader;
import net.realsee.gcl.domain.bo.mes.*;
import net.realsee.gcl.service.IGclMesService;
import net.realsee.service.wcs.data.IWcsZoneTaskTempService;
import net.realsee.service.wcs.rcs.hik.IHikV4Service;
import net.realsee.service.wcs.warehouse.IWcsWarehouseLocationService;
import net.realsee.service.wcs.work.IWcsOrderDetailService;
import net.realsee.service.wcs.work.IWcsOrderHeaderService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;

/**
 * 协鑫MES接口实现
 *
 * @author
 * @since 2023/8/22 13:28
 */
@Slf4j
@RequiredArgsConstructor
@Service
public class GclMesServiceImpl implements IGclMesService {

    private final IWcsOrderHeaderService orderHeaderService;
    private final IWcsOrderDetailService orderDetailService;
    private final IWcsWarehouseLocationService locationService;
    private final IWcsZoneTaskTempService zoneTaskTempService;

    private final IHikV4Service hikV4Service;

    /**
     * 任务创建
     */
    @Transactional
    @Override
    public GclMesOperateResult taskCreate(GclMesTaskCreateReq taskCreateReq) {
        // 协鑫MES任务创建结果
        GclMesOperateResult mesTaskCreateResult = GclMesOperateResult.ok();
        // 入站日志待更新对象
        InterfaceResponse inLogForUpdate = InterfaceResponse.builder()
            .taskId(taskCreateReq.getId())
            .keyword1(taskCreateReq.getId())
            .keyword2(taskCreateReq.getOption1())
            .content(JSONUtil.toJsonStr(mesTaskCreateResult))
            .httpCode((long) HttpStatus.SUCCESS)
            .status(InterfaceResultCodeEnum.SUCCESS.getCode())
            .build();
        try {
            this.handleMesTaskCreate(taskCreateReq);
        } catch (GclMesTaskException e) {
            // 异常时，设置错误信息
            inLogForUpdate.setHttpCode((long) HttpStatus.ERROR).setStatus(InterfaceResultCodeEnum.FAILED.getCode())
                .setContent(JSONUtil.toJsonStr(mesTaskCreateResult.setResult("NG").setErrorCode(e.getCodeEnum().getCode()).setMessage(e.getErrorMessage())));
            throw e;
        } catch (Exception e) {
            // 异常时，设置错误信息
            inLogForUpdate.setHttpCode((long) HttpStatus.ERROR).setStatus(InterfaceResultCodeEnum.FAILED.getCode()).setContent(e.getMessage());
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.UNKNOWN_ERR);
        } finally {
            // 更新入站日志内容
            InterfaceUtils.updateInlogByResponse(inLogForUpdate);
        }
        return mesTaskCreateResult;
    }

    /**
     * mes任务创建处理
     */
    private void handleMesTaskCreate(GclMesTaskCreateReq taskCreateReq) {
        String orderNo = taskCreateReq.getId();
        String startLocation = taskCreateReq.getStartLocation();
        String endLocation = taskCreateReq.getEndLocation();
        String priority = taskCreateReq.getPriority();
        String mesCreateTime = taskCreateReq.getDateTime();
        String taskType = taskCreateReq.getOption1(); // 此字段目前用作 MES接口编码

        // 1.判断两个库位信息是否存在
        LocationFullInfoBo startLocationInfo = locationService.getFullInfoByLocation(startLocation);
        if (ObjectUtil.isNull(startLocationInfo) || StrUtil.isBlank(startLocationInfo.getMapMark())) {
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.START_MAPMARK_NOT_EXIST);
        }
        LocationFullInfoBo endLocationInfo = locationService.getFullInfoByLocation(endLocation);
        if (ObjectUtil.isNull(endLocationInfo) || StrUtil.isBlank(endLocationInfo.getMapMark())) {
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.END_MAPMARK_NOT_EXIST);
        }

        // 2.判断1010的单据头是否存在
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(orderNo, OrderTypeEnums.BUSINESS.getCode());
        if (ObjectUtil.isNull(orderHeader)) {
            // 2.1 不存在，则创建1010单据头、1010单据明细、1020单据头、1020单据明细、调RCS任务生成接口
            this.mesTaskFirstDispatch(orderNo, priority, mesCreateTime, startLocationInfo, endLocationInfo, taskType);
        } else {
            // 2.2 存在，则去判断是任务重复了，还是下立棒的二段任务
            WcsOrderDetail orderDetail = orderDetailService.getByOrderNoAndOrderTypeAndToLocation(orderNo, OrderTypeEnums.BUSINESS.getCode(), endLocation);
            if (ObjectUtil.isNull(orderDetail)) {
                // 2.2.1 是下立棒的二段任务，则创建1010单据明细、1020单据明细、调RCS任务继续接口
                this.mesTaskSecondDispatch(orderNo, startLocationInfo, endLocationInfo, taskType);
            } else {
                // 2.2.2 重复，则抛出
                throw new GclMesTaskException(GclMesTaskException.CodeEnum.TASK_REPEAT);
            }
        }
    }

    /**
     * mes任务首次下发
     */
    private void mesTaskFirstDispatch(String orderNo, String priority, String mesCreateTime,
                                      LocationFullInfoBo startLocationInfo, LocationFullInfoBo endLocationInfo, String taskType) {
        // 查询模板编码
        WcsZoneTaskTemp taskTemp = zoneTaskTempService.getByWarehouseAndZone(startLocationInfo.getWarehouse(),
            startLocationInfo.getZone(), endLocationInfo.getWarehouse(), endLocationInfo.getZone());
        if (ObjectUtil.isNull(taskTemp) || StrUtil.isBlank(taskTemp.getTaskTempCode())) {
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.TASK_TEMP_NOT_EXIST);
        }
        String taskTempCode = taskTemp.getTaskTempCode();

        // 新增单据头
        this.createOrderHeader(orderNo, priority, mesCreateTime, endLocationInfo, taskTempCode, taskType);
        // 新增单据明细
        this.createOrderDetail(orderNo, startLocationInfo, endLocationInfo, taskType);
        // 创建RCS任务
        HikRcsOperateResult hikRcsOperateResult = hikV4Service.taskSubmit(orderNo, taskTempCode,
            startLocationInfo.getMapMark(), endLocationInfo.getWaitMapMark(), endLocationInfo.getMapMark(), Integer.valueOf(priority));
        // 校验RCS执行结果
        checkRcsTaskExecuteResult(hikRcsOperateResult);
    }

    /**
     * mes任务二次下发
     */
    private void mesTaskSecondDispatch(String orderNo, LocationFullInfoBo startLocationInfo, LocationFullInfoBo endLocationInfo, String taskType) {
        // 新增单据明细
        this.createOrderDetail(orderNo, startLocationInfo, endLocationInfo, taskType);
        // RCS继续执行任务
        HikRcsOperateResult hikRcsOperateResult = hikV4Service.taskContinue(orderNo, endLocationInfo.getMapMark());
        // 校验RCS执行结果
        checkRcsTaskExecuteResult(hikRcsOperateResult);
        // 更新单据头上的类型为二段任务类型
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(orderNo, OrderTypeEnums.BUSINESS.getCode());
        orderHeader.setTaskType(taskType);
        orderHeaderService.updateById(orderHeader);
        orderHeader = orderHeaderService.getByOrderNoAndOrderType(orderNo, OrderTypeEnums.UP_DOWN.getCode());
        orderHeader.setTaskType(taskType);
        orderHeaderService.updateById(orderHeader);
    }

    /**
     * 新增单据头
     */
    private void createOrderHeader(String orderNo, String priority, String mesCreateTime, LocationFullInfoBo endLocationInfo, String taskTempCode, String taskType) {
        // 创建1010单据头
        WcsOrderHeader orderHeader = new WcsOrderHeader();
        orderHeader.setOrderNo(orderNo);
        orderHeader.setOrderType(OrderTypeEnums.BUSINESS.getCode());
        orderHeader.setTaskType(taskType);
        orderHeader.setWorkshop(endLocationInfo.getWorkshop());
        orderHeader.setFacility(endLocationInfo.getFacility());
        orderHeader.setSourceClass(OrderSourceEnums.IF.getCode());
        orderHeader.setSourceAppCode(OrderSourceClassEnums.MES.getCode());
        orderHeader.setSourceTaskCode(orderNo);
        orderHeader.setPriority(Long.parseLong(priority));
        orderHeader.setSourceCreateOn(DateUtil.parse(mesCreateTime));
        orderHeader.setOrderStatus(OrderStatusEnums.CREATED.getCode());
        orderHeader.setProgressStatus(OrderProcessStatusEnum._1010010.getCode());
        orderHeader.setExecOrderNo(orderNo);
        orderHeader.setTaskTemp(taskTempCode);
        orderHeader.setStartTime(DateUtil.date());
        orderHeaderService.save(orderHeader);
        // 创建1020的单据头
        orderHeader.setId(null);
        orderHeader.setOrderType(OrderTypeEnums.UP_DOWN.getCode());
        orderHeaderService.save(orderHeader);
    }

    /**
     * 新增单据明细
     */
    private void createOrderDetail(String orderNo, LocationFullInfoBo startLocationInfo, LocationFullInfoBo endLocationInfo, String taskType) {
        // 创建1010的单据明细
        WcsOrderDetail orderDetail = new WcsOrderDetail();
        orderDetail.setOrderNo(orderNo);
        orderDetail.setOrderType(OrderTypeEnums.BUSINESS.getCode());
        orderDetail.setTaskType(taskType);
        orderDetail.setFromWarehouse(startLocationInfo.getWarehouse());
        orderDetail.setFromLocation(startLocationInfo.getLocation());
        orderDetail.setToWarehouse(endLocationInfo.getWarehouse());
        orderDetail.setToLocation(endLocationInfo.getLocation());
        orderDetail.setProgressStatus(OrderProcessStatusEnum._1010020.getCode());
        orderDetail.setMapMark(endLocationInfo.getMapMark());
        orderDetail.setStartTime(DateUtil.date());
        orderDetailService.save(orderDetail);
        // 创建1020的单据明细
        orderDetail.setId(null);
        orderDetail.setOrderType(OrderTypeEnums.UP_DOWN.getCode());
        orderDetailService.save(orderDetail);
    }

    /**
     * 任务取消
     */
    @Transactional
    @Override
    public GclMesOperateResult taskCancel(GclMesTaskCancelReq taskCancelReq) {
        // 协鑫MES任务创建结果
        GclMesOperateResult mesTaskCancelResult = GclMesOperateResult.ok();
        // 入站日志待更新对象
        InterfaceResponse inLogForUpdate = InterfaceResponse.builder()
            .taskId(taskCancelReq.getId())
            .keyword1(taskCancelReq.getId())
            .content(JSONUtil.toJsonStr(mesTaskCancelResult))
            .httpCode((long) HttpStatus.SUCCESS)
            .status(InterfaceResultCodeEnum.SUCCESS.getCode())
            .build();
        try {
            this.handleMesTaskCancel(taskCancelReq);
        } catch (GclMesTaskException e) {
            // 异常时，设置错误信息
            inLogForUpdate.setHttpCode((long) HttpStatus.ERROR).setStatus(InterfaceResultCodeEnum.FAILED.getCode())
                .setContent(JSONUtil.toJsonStr(mesTaskCancelResult.setResult("NG").setErrorCode(e.getCodeEnum().getCode()).setMessage(e.getErrorMessage())));
            throw e;
        } catch (Exception e) {
            // 异常时，设置错误信息
            inLogForUpdate.setHttpCode((long) HttpStatus.ERROR).setStatus(InterfaceResultCodeEnum.FAILED.getCode()).setContent(e.getMessage());
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.UNKNOWN_ERR);
        } finally {
            // 更新入站日志内容
            InterfaceUtils.updateInlogByResponse(inLogForUpdate);
        }
        return mesTaskCancelResult;
    }

    private void handleMesTaskCancel(GclMesTaskCancelReq taskCancelReq) {
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(taskCancelReq.getId(), OrderTypeEnums.UP_DOWN.getCode());
        // 任务存在判断
        if (ObjectUtil.isNull(orderHeader)) {
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.TASK_NOT_EXIST);
        }
        // 任务执行中判断
        if (ObjectUtil.equals(orderHeader.getProgressStatus(), OrderProcessStatusEnum._1010020.getCode())
            && StrUtil.equals(taskCancelReq.getRunFlag(), GclMesConstants.TASK_CANCEL_SOFT)) {
            // 任务执行，软取消则抛异常
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.TASK_EXECUTING);
        }
        // 任务执行完成判断
        if (ObjectUtil.equals(orderHeader.getOrderStatus(), OrderStatusEnums.COMPLETE.getCode())
            || ObjectUtil.equals(orderHeader.getOrderStatus(), OrderStatusEnums.CANCEL.getCode())) {
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.TASK_COMPLETED);
        }
        // RCS取消任务
        HikRcsOperateResult hikRcsOperateResult = hikV4Service.taskCancel(orderHeader.getOrderNo());
        checkRcsTaskExecuteResult(hikRcsOperateResult);
        // 更新WCS中任务状态
        orderHeader.setOrderStatus(OrderStatusEnums.CANCEL.getCode());
        orderHeaderService.updateById(orderHeader);
    }

    /**
     * 修改优先级
     */
    @Override
    public GclMesOperateResult taskUpdate(GclMesTaskUpdateReq taskUpdateReq) {
        // 协鑫MES任务创建结果
        GclMesOperateResult mesTaskUpdateResult = GclMesOperateResult.ok();
        // 入站日志待更新对象
        InterfaceResponse inLogForUpdate = InterfaceResponse.builder()
            .taskId(taskUpdateReq.getId())
            .keyword1(taskUpdateReq.getId())
            .content(JSONUtil.toJsonStr(mesTaskUpdateResult))
            .httpCode((long) HttpStatus.SUCCESS)
            .status(InterfaceResultCodeEnum.SUCCESS.getCode())
            .build();
        try {
            this.handleMesTaskSetPriority(taskUpdateReq);
        } catch (GclMesTaskException e) {
            // 异常时，设置错误信息
            inLogForUpdate.setHttpCode((long) HttpStatus.ERROR).setStatus(InterfaceResultCodeEnum.FAILED.getCode())
                .setContent(JSONUtil.toJsonStr(mesTaskUpdateResult.setResult("NG").setErrorCode(e.getCodeEnum().getCode()).setMessage(e.getErrorMessage())));
            throw e;
        } catch (Exception e) {
            // 异常时，设置错误信息
            inLogForUpdate.setHttpCode((long) HttpStatus.ERROR).setStatus(InterfaceResultCodeEnum.FAILED.getCode()).setContent(e.getMessage());
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.UNKNOWN_ERR);
        } finally {
            // 更新入站日志内容
            InterfaceUtils.updateInlogByResponse(inLogForUpdate);
        }
        return mesTaskUpdateResult;
    }

    private void handleMesTaskSetPriority(GclMesTaskUpdateReq taskUpdateReq) {
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(taskUpdateReq.getId(), OrderTypeEnums.UP_DOWN.getCode());
        // 任务存在判断
        if (ObjectUtil.isNull(orderHeader)) {
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.TASK_NOT_EXIST);
        }
        // 更新RCS中任务优先级
        HikRcsOperateResult hikRcsOperateResult = hikV4Service.setTaskPriority(taskUpdateReq.getId(), Integer.valueOf(taskUpdateReq.getPriority()));
        checkRcsTaskExecuteResult(hikRcsOperateResult);
        // 更新WCS中任务优先级
        orderHeader.setPriority(Long.parseLong(taskUpdateReq.getPriority()));
        orderHeaderService.updateById(orderHeader);
    }

    /**
     * 校验RCS任务执行结构
     */
    private static void checkRcsTaskExecuteResult(HikRcsOperateResult hikRcsOperateResult) {
        if (ObjectUtil.isNull(hikRcsOperateResult)) {
            // 下发失败
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.NETWORK_ERR);
        }
        if (!hikRcsOperateResult.isSuccess()) {
            // 执行失败
            throw new GclMesTaskException(GclMesTaskException.CodeEnum.UNKNOWN_ERR, hikRcsOperateResult.getMessage());
        }
    }

    /**
     * 任务状态上报（发送MES）
     */
    @Override
    public GclMesOperateResult taskStatusReportToMes(GclMesTaskStatusReportReq taskStatusReportReq) {
        GclMesOperateResult result = null;
        taskStatusReportReq.setDateTime(DateUtil.format(DateUtil.date(), "yyyy/MM/dd HH:mm:ss"));
        HttpResponse httpResponse = InterfaceUtils.callByHttp(
            InterfaceRequest.builder()
                .interfaceCode(InterfaceCodeEnum.GCL_MES_TASK_STATUS_REPORT)
                .taskId(taskStatusReportReq.getId())
                .keyword1(taskStatusReportReq.getId())
                .keyword2("runFlag = " + taskStatusReportReq.getRunFlag())
                .bodyStr(JSONUtil.toJsonStr(taskStatusReportReq))
                .headerObj(new HashMap<String, String>() {{
                    put("Key", taskStatusReportReq.getId());
                }})
                .convertObj(new HashMap<String, String>() {{
                    put("taskType", taskStatusReportReq.getMethod());
                }})
                .build());
        if (ObjectUtil.isNotNull(httpResponse) && httpResponse.isOk()) {
            // 首字符大写，无法解析
            // result = JSONUtil.toBean(httpResponse.body(), GclMesOperateResult.class);
            JSONObject jsonObject = JSONUtil.parseObj(httpResponse.body());
            result = new GclMesOperateResult(jsonObject.getStr("Result"), jsonObject.getStr("ErrorCode"), jsonObject.getStr("Message"));
        }
        return result;
    }

    /**
     * 任务取消（发送MES）
     */
    @Override
    public GclMesOperateResult taskCancelToMes(String taskCode) {
        WcsOrderHeader orderHeader = orderHeaderService.getByOrderNoAndOrderType(taskCode, OrderTypeEnums.UP_DOWN.getCode());

        GclMesTaskStatusReportReq cancelReq = new GclMesTaskStatusReportReq();
        cancelReq.setId(taskCode);
        cancelReq.setRunFlag(GclMesRunFlagEnum.TASK_WARN.getCode());
        cancelReq.setAgvNo(orderHeader.getVehicleNo());
        cancelReq.setErrorCode(GclMesTaskException.CodeEnum.TASK_CANCEL.getCode());
        cancelReq.setMethod(orderHeader.getTaskType());
        return taskStatusReportToMes(cancelReq);
    }

    /**
     * 脱胶下工装完成通知（发送MES）
     */
    @Override
    public GclMesOperateResult tjMachineToolOutToMes(String machine) {
        GclMesOperateResult result = null;
        GclMesTJMachineToolOutReq toolOutReq = new GclMesTJMachineToolOutReq();
        toolOutReq.setId(IdUtil.getSnowflakeNextIdStr());
        toolOutReq.setMachine(machine);
        toolOutReq.setDateTime(DateUtil.format(DateUtil.date(), "yyyy/MM/dd HH:mm:ss"));
        HttpResponse httpResponse = InterfaceUtils.callByHttp(
            InterfaceRequest.builder()
                .interfaceCode(InterfaceCodeEnum.GCL_MES_TJ_MACHINE_TOOL_OUT_REPORT)
                .taskId(toolOutReq.getId())
                .keyword1(InterfaceCodeEnum.GCL_MES_TJ_MACHINE_TOOL_OUT_REPORT.getCode())
                .keyword2(InterfaceCodeEnum.GCL_MES_TJ_MACHINE_TOOL_OUT_REPORT.getMessage())
                .bodyStr(JSONUtil.toJsonStr(toolOutReq))
                .headerObj(new HashMap<String, String>() {{
                    put("Key", toolOutReq.getId());
                }})
                .build());
        if (ObjectUtil.isNotNull(httpResponse) && httpResponse.isOk()) {
            // 首字符大写，无法解析
            JSONObject jsonObject = JSONUtil.parseObj(httpResponse.body());
            result = new GclMesOperateResult(jsonObject.getStr("Result"), jsonObject.getStr("ErrorCode"), jsonObject.getStr("Message"));
        }
        return result;
    }
}
